/**
 * File: GraphAStatistic.java
 * Created by: mhaimel
 * Created on: Nov 24, 2010
 * CVS:  $Id: GraphAStatistic.java 1.0 Nov 24, 2010 1:28:36 PM mhaimel Exp $
 */
package uk.ac.ebi.curtain.statistic;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import uk.ac.ebi.curtain.model.graph.Graph;
import uk.ac.ebi.curtain.model.graph.Node;
import uk.ac.ebi.curtain.model.graph.GraphAccess.TouchEach;
import uk.ac.ebi.curtain.model.graph.curtain.ContigInfo;
import uk.ac.ebi.curtain.model.graph.curtain.CurtainGraph;
import uk.ac.ebi.curtain.model.graph.curtain.ReadWrapper;

/**
 * @author mhaimel
 *
 */
public class GraphAStatistic {
	private final Log log = LogFactory.getLog(this.getClass());
	
	private static final Integer DEFAULT_THRESHOLD = 17; 
	
	private final CurtainGraph graph;
	private final AtomicInteger threshold = new AtomicInteger(DEFAULT_THRESHOLD);  
	private final ConcurrentSkipListMap<Double, Collection<Node<ContigInfo, ReadWrapper>>> map = new ConcurrentSkipListMap<Double, Collection<Node<ContigInfo, ReadWrapper>>>();
	
	
	public GraphAStatistic(CurtainGraph graph) {
		this.graph = graph;
	}
	
	public void setThreshold(int threshold) {
		this.threshold.set(threshold);
	}
	
	public int getThreshold() {
		return threshold.get();
	}
	
	public void process(){
		getLog().debug("Process A-statistics ...");
		final int cat = getCurtainGraph().getCategories();
		final AtomicLong sum = new AtomicLong(0l);
		final AtomicInteger cnt = new AtomicInteger(0);
		
		getGraph().processNodes(new TouchEach<Node<ContigInfo, ReadWrapper>>(){

			@Override
			public void touch(Node<ContigInfo, ReadWrapper> node) {
				if(node.getValue().getLength() > 1000){
					sum.addAndGet(node.getValue().getLength());
					for (int i = 0; i < cat; ++i){
						cnt.addAndGet((int) node.getValue().calculateSequenceCoverage(i).intValue());
					}
				}
		}});
		
		final double l1 = (cnt.doubleValue()/sum.doubleValue());
		final AtomicLong inplay = new AtomicLong(0);
		final AtomicLong total = new AtomicLong(0);
		final AtomicLong inTotal = new AtomicLong(0);
		
		graph.getGraph().processNodes(new TouchEach<Node<ContigInfo, ReadWrapper>>(){

			@Override
			public void touch(Node<ContigInfo, ReadWrapper> node) {
				Long len = node.getValue().getLength();
				total.addAndGet(len);
				double full = 0d;
				for (int i = 0; i < cat; ++i){
					full += node.getValue().calculateSequenceCoverage(i);
				}
				double left = len.doubleValue()*l1;
				double right = full*(Math.log(2));
				Double res = left-right;
				if(res > getThreshold()){
					Collection<Node<ContigInfo, ReadWrapper>> col = getMapEntry(res);
					col.add(node);
					inTotal.addAndGet(len);
					inplay.incrementAndGet();
				}
		}});

		double pc = total.doubleValue() > 0?inTotal.doubleValue() / total.doubleValue():0;
		getLog().debug("A-statistics: " + inplay+ "("+ inTotal+ ") out of " + getGraph().getNodeSize()+"("+total + ") chosen!! " + pc);
	}
	
	private Collection<Node<ContigInfo, ReadWrapper>> getMapEntry(Double res) {
		Collection<Node<ContigInfo, ReadWrapper>> col = this.map.get(res);
		if(null == col){
			col = new LinkedList<Node<ContigInfo, ReadWrapper>>();
			this.map.put(res, col);
		}
		return col;
	}
	
	public List<Node<ContigInfo, ReadWrapper>> getUniqueNodes() {
		List<Node<ContigInfo, ReadWrapper>> nodes = new LinkedList<Node<ContigInfo,ReadWrapper>>();
		for(Entry<Double, Collection<Node<ContigInfo, ReadWrapper>>> entry : this.map.descendingMap().entrySet()){
			nodes.addAll(entry.getValue());
		}
		return nodes;
	}
	
	private CurtainGraph getCurtainGraph() {
		return graph;
	}
	
	private Graph<ContigInfo, ReadWrapper> getGraph() {
		return graph.getGraph();
	}
	
	private Log getLog() {
		return log;
	}
	
	public void clean(){
		this.map.clear();
	}
	
}
